package io.gitee.zhangbinhub.acp.core.common.security.key

import io.gitee.zhangbinhub.acp.core.common.base.BaseEncrypt
import io.gitee.zhangbinhub.acp.core.common.log.LogFactory
import io.gitee.zhangbinhub.acp.core.common.security.ByteEncodeEnum
import io.gitee.zhangbinhub.acp.core.common.security.SM2Encrypt
import org.apache.commons.text.CharacterPredicates
import org.apache.commons.text.RandomStringGenerator
import org.bouncycastle.asn1.gm.GMNamedCurves
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.crypto.generators.ECKeyPairGenerator
import org.bouncycastle.crypto.params.ECDomainParameters
import org.bouncycastle.crypto.params.ECKeyGenerationParameters
import org.bouncycastle.crypto.params.ECPrivateKeyParameters
import org.bouncycastle.crypto.params.ECPublicKeyParameters
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.openssl.PEMKeyPair
import org.bouncycastle.openssl.PEMParser
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
import java.io.*
import java.math.BigInteger
import java.security.*
import java.security.interfaces.DSAPrivateKey
import java.security.interfaces.DSAPublicKey
import java.security.interfaces.RSAPrivateKey
import java.security.interfaces.RSAPublicKey
import java.security.spec.*
import javax.crypto.spec.SecretKeySpec

/**
 * @author zhang by 10/07/2019
 * @since JDK 11
 */
object KeyManagement : BaseEncrypt() {

    private val log = LogFactory.getInstance(KeyManagement::class.java)

    const val RANDOM_STR = 0

    const val RANDOM_CHAR = 1

    const val RANDOM_NUMBER = 2

    init {
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Security.addProvider(BouncyCastleProvider())
        }
    }

    /**
     * 生成RSA公钥和私钥
     *
     * @return Object[] [0]:RSAPublicKey,[1]:RSAPrivateKey
     */
    @JvmStatic
    @Throws(NoSuchAlgorithmException::class)
    fun getRsaKeys(): Array<Any> {
        val keyPairGen = KeyPairGenerator.getInstance("RSA")
        keyPairGen.initialize(2048)
        val keyPair = keyPairGen.generateKeyPair()
        val publicKey = keyPair.public as RSAPublicKey
        val privateKey = keyPair.private as RSAPrivateKey
        return arrayOf(publicKey, privateKey)
    }

    /**
     * 生成DSA公钥和私钥
     *
     * @return Object[] [0]:DSAPublicKey,[1]:DSAPrivateKey
     */
    @JvmStatic
    @Throws(NoSuchAlgorithmException::class, UnsupportedEncodingException::class)
    fun getDsaKeys(): Array<Any> {
        val keyPairGen = KeyPairGenerator.getInstance("DSA")
        val secureRandom = SecureRandom()
        secureRandom.setSeed(decodeToBytes(getRandomString(RANDOM_STR, 32), ByteEncodeEnum.BYTE))
        keyPairGen.initialize(1024, secureRandom)
        val keyPair = keyPairGen.generateKeyPair()
        val publicKey = keyPair.public as DSAPublicKey
        val privateKey = keyPair.private as DSAPrivateKey
        return arrayOf(publicKey, privateKey)
    }

    /**
     * 生成SM2公钥和私钥
     *
     * @return Object[] [0]:BCECPublicKey,[1]:BCECPrivateKey
     */
    @JvmStatic
    @Throws(NoSuchAlgorithmException::class)
    @JvmOverloads
    fun getSM2Keys(
        publicKeyEncode: ByteEncodeEnum = ByteEncodeEnum.HEX, privateKeyRadix: Int = SM2Encrypt.PRIVATE_KEY_RADIX
    ): Array<String> {
        val sm2ECParameters = GMNamedCurves.getByName(SM2Encrypt.SM2_STD)
        val domainParameters = ECDomainParameters(
            sm2ECParameters.curve, sm2ECParameters.g, sm2ECParameters.n
        )
        val keyPairGenerator = ECKeyPairGenerator()
        val secureRandom = SecureRandom()
        secureRandom.setSeed(decodeToBytes(getRandomString(RANDOM_STR, 32), ByteEncodeEnum.BYTE))
        keyPairGenerator.init(ECKeyGenerationParameters(domainParameters, secureRandom))
        val keyPair = keyPairGenerator.generateKeyPair()
        return arrayOf(
            encodeToString((keyPair.public as ECPublicKeyParameters).q.getEncoded(false), publicKeyEncode),
            (keyPair.private as ECPrivateKeyParameters).d.toString(privateKeyRadix)
        )
    }

    /**
     * 生成AES密钥
     *
     * @param keyStr 长度不超过16位的字符串
     * @return 密钥
     */
    @JvmStatic
    @Throws(UnsupportedEncodingException::class)
    @JvmOverloads
    fun getAESKey(keyStr: String, keyEncode: ByteEncodeEnum = ByteEncodeEnum.BYTE): Key =
        getKey(keyStr, "AES", keyEncode)

    /**
     * 生成DES密钥
     *
     * @param keyStr 长度不超过24位的字符串
     * @return 密钥
     */
    @JvmStatic
    @Throws(UnsupportedEncodingException::class)
    @JvmOverloads
    fun getDESKey(keyStr: String, keyEncode: ByteEncodeEnum = ByteEncodeEnum.BYTE): Key =
        getKey(keyStr, "DES", keyEncode)

    /**
     * 生成3DES密钥
     *
     * @param keyStr 长度不超过24位的字符串
     * @return 密钥
     */
    @JvmStatic
    @Throws(UnsupportedEncodingException::class)
    @JvmOverloads
    fun get3DESKey(keyStr: String, keyEncode: ByteEncodeEnum = ByteEncodeEnum.BYTE): Key =
        getKey(keyStr, "DESede", keyEncode)

    /**
     * 生成SM4密钥
     *
     * @param keyStr 长度不超过16位的字符串
     * @return 密钥
     */
    @JvmStatic
    @Throws(UnsupportedEncodingException::class)
    @JvmOverloads
    fun getSM4Key(keyStr: String, keyEncode: ByteEncodeEnum = ByteEncodeEnum.BYTE): Key =
        getKey(keyStr, "SM4", keyEncode)

    /**
     * 生成密钥
     *
     * @param keyStr    密钥字符串
     * @param cryptType 加密算法
     * @return 密钥
     * @throws UnsupportedEncodingException 异常
     */
    @JvmStatic
    @Throws(UnsupportedEncodingException::class)
    @JvmOverloads
    fun getKey(keyStr: String, cryptType: String, keyEncode: ByteEncodeEnum = ByteEncodeEnum.BYTE): Key =
        SecretKeySpec(decodeToBytes(keyStr, keyEncode), cryptType)

    /**
     * 使用模和指数生成RSA公钥
     * 注意：【此代码用了默认补位方式，为RSA/None/PKCS1Padding，不同JDK默认的补位方式可能不同，如Android默认是RSA/None/NoPadding】
     *
     * @param modulus  模
     * @param exponent 指数
     * @param radix    基数 2，8，10，16
     * @return 公钥
     */
    @JvmStatic
    @Throws(NoSuchAlgorithmException::class, InvalidKeySpecException::class)
    @JvmOverloads
    fun getRSAPublicKey(modulus: String, exponent: String, radix: Int = 10): RSAPublicKey {
        val b1 = BigInteger(modulus, radix)
        val b2 = BigInteger(exponent, radix)
        val keyFactory = KeyFactory.getInstance("RSA")
        val keySpec = RSAPublicKeySpec(b1, b2)
        return keyFactory.generatePublic(keySpec) as RSAPublicKey
    }

    /**
     * 使用模和指数生成RSA私钥
     * 注意：【此代码用了默认补位方式，为RSA/None/PKCS1Padding，不同JDK默认的补位方式可能不同，如Android默认是RSA/None/NoPadding】
     *
     * @param modulus  模
     * @param exponent 指数
     * @param radix    基数 2，8，10，16
     * @return 私钥
     */
    @JvmStatic
    @Throws(NoSuchAlgorithmException::class, InvalidKeySpecException::class)
    @JvmOverloads
    fun getRSAPrivateKey(modulus: String, exponent: String, radix: Int = 10): RSAPrivateKey {
        val b1 = BigInteger(modulus, radix)
        val b2 = BigInteger(exponent, radix)
        val keyFactory = KeyFactory.getInstance("RSA")
        val keySpec = RSAPrivateKeySpec(b1, b2)
        return keyFactory.generatePrivate(keySpec) as RSAPrivateKey
    }

    /**
     * 解析DER编码的公钥证书
     *
     * @param filePath 证书文件绝对路径
     * @return RSA公钥对象
     */
    @JvmStatic
    @Throws(InvalidKeySpecException::class, NoSuchAlgorithmException::class, IOException::class)
    fun getRSAPublicKeyForDER(filePath: String): RSAPublicKey {
        val key = getFileContent(filePath)
        val keySpec = X509EncodedKeySpec(key)
        val keyFactory = KeyFactory.getInstance("RSA")
        return keyFactory.generatePublic(keySpec) as RSAPublicKey
    }

    /**
     * 解析DER编码的私钥证书
     *
     * @param filePath 证书文件绝对路径
     * @return RSA私钥对象
     */
    @JvmStatic
    @Throws(NoSuchAlgorithmException::class, InvalidKeySpecException::class, IOException::class)
    fun getRSAPrivateKeyForDER(filePath: String): RSAPrivateKey {
        val key = getFileContent(filePath)
        val keySpec = PKCS8EncodedKeySpec(key)
        val keyFactory = KeyFactory.getInstance("RSA")
        return keyFactory.generatePrivate(keySpec) as RSAPrivateKey
    }

    /**
     * 解析PEM编码的公钥证书
     *
     * @param filePath 证书文件绝对路径
     * @return RSA公钥对象
     */
    @JvmStatic
    @Throws(IOException::class)
    fun getRSAPublicKeyForPEM(filePath: String): RSAPublicKey? {
        val rsaPublic = FileReader(filePath)
        val publicPem = PEMParser(rsaPublic)
        val pubObject = publicPem.readObject()
        val pemConverter = JcaPEMKeyConverter()
        pemConverter.setProvider(BouncyCastleProvider.PROVIDER_NAME)
        return if (pubObject is SubjectPublicKeyInfo) {
            pemConverter.getPublicKey(pubObject) as RSAPublicKey
        } else {
            null
        }
    }

    /**
     * 解析PEM编码的私钥证书
     *
     * @param filePath 证书文件绝对路径
     * @return RSA私钥对象
     */
    @JvmStatic
    @Throws(IOException::class)
    fun getRSAPrivateKeyForPEM(filePath: String): RSAPrivateKey? {
        val rsaReader = FileReader(filePath)
        val privatePem = PEMParser(rsaReader)
        val obj = privatePem.readObject()
        val pemConverter = JcaPEMKeyConverter()
        pemConverter.setProvider(BouncyCastleProvider.PROVIDER_NAME)
        return if (obj is PEMKeyPair) {
            val keyPair = pemConverter.getKeyPair(obj)
            keyPair.private as RSAPrivateKey
        } else {
            if (obj is KeyPair) obj.private as RSAPrivateKey else null
        }
    }

    /**
     * 解析SSH生成的公钥证书
     *
     * @param keyStr 公钥字符串
     * @return RSA公钥对象
     */
    @JvmStatic
    @Throws(Exception::class)
    @JvmOverloads
    fun getRSAPublicKeyForSSH(keyStr: String, keyEncode: ByteEncodeEnum = ByteEncodeEnum.BASE64): RSAPublicKey {
        val key = decodeToBytes(keyStr, keyEncode)
        val sshRsa = byteArrayOf(
            0,
            0,
            0,
            7,
            's'.code.toByte(),
            's'.code.toByte(),
            'h'.code.toByte(),
            '-'.code.toByte(),
            'r'.code.toByte(),
            's'.code.toByte(),
            'a'.code.toByte()
        )
        var startIndex = sshRsa.size/* Decode the public exponent */
        var len = decodeUInt32(key, startIndex)
        startIndex += 4
        val peB = ByteArray(len)
        for (i in 0 until len) {
            peB[i] = key[startIndex++]
        }
        val pe = BigInteger(peB)/* Decode the modulus */
        len = decodeUInt32(key, startIndex)
        startIndex += 4
        val mdB = ByteArray(len)
        for (i in 0 until len) {
            mdB[i] = key[startIndex++]
        }
        val md = BigInteger(mdB)
        val keyFactory = KeyFactory.getInstance("RSA")
        val ks = RSAPublicKeySpec(md, pe)
        return keyFactory.generatePublic(ks) as RSAPublicKey
    }

    /**
     * 生成DSA公钥
     *
     * @param publicKey y
     * @param prime     p
     * @param subPrime  q
     * @param base      g
     * @param radix     基数 2，8，10，16
     * @return 公钥
     */
    @JvmStatic
    @Throws(NoSuchAlgorithmException::class, InvalidKeySpecException::class)
    @JvmOverloads
    fun getDSAPublicKey(
        publicKey: String, prime: String, subPrime: String, base: String, radix: Int = 10
    ): DSAPublicKey {
        val y = BigInteger(publicKey, radix)
        val p = BigInteger(prime, radix)
        val q = BigInteger(subPrime, radix)
        val g = BigInteger(base, radix)
        val keyFactory = KeyFactory.getInstance("DSA")
        val keySpec = DSAPublicKeySpec(y, p, q, g)
        return keyFactory.generatePublic(keySpec) as DSAPublicKey
    }

    /**
     * 使用模和指数生成DSA私钥
     *
     * @param privateKey x
     * @param prime      p
     * @param subPrime   q
     * @param base       g
     * @param radix      基数 2，8，10，16
     * @return 私钥
     */
    @JvmStatic
    @Throws(NoSuchAlgorithmException::class, InvalidKeySpecException::class)
    @JvmOverloads
    fun getDSAPrivateKey(
        privateKey: String, prime: String, subPrime: String, base: String, radix: Int = 10
    ): DSAPrivateKey {
        val x = BigInteger(privateKey, radix)
        val p = BigInteger(prime, radix)
        val q = BigInteger(subPrime, radix)
        val g = BigInteger(base, radix)
        val keyFactory = KeyFactory.getInstance("DSA")
        val keySpec = DSAPrivateKeySpec(x, p, q, g)
        return keyFactory.generatePrivate(keySpec) as DSAPrivateKey
    }

    /**
     * 解析DER编码的公钥证书
     *
     * @param filePath 证书文件绝对路径
     * @return DSA公钥对象
     */
    @JvmStatic
    @Throws(InvalidKeySpecException::class, NoSuchAlgorithmException::class, IOException::class)
    fun getDSAPublicKeyForDER(filePath: String): DSAPublicKey {
        val key = getFileContent(filePath)
        val keySpec = X509EncodedKeySpec(key)
        val keyFactory = KeyFactory.getInstance("DSA")
        return keyFactory.generatePublic(keySpec) as DSAPublicKey
    }

    /**
     * 解析DER编码的私钥证书
     *
     * @param filePath 证书文件绝对路径
     * @return DSA私钥对象
     */
    @JvmStatic
    @Throws(NoSuchAlgorithmException::class, InvalidKeySpecException::class, IOException::class)
    fun getDSAPrivateKeyForDER(filePath: String): DSAPrivateKey {
        val key = getFileContent(filePath)
        val keySpec = PKCS8EncodedKeySpec(key)
        val keyFactory = KeyFactory.getInstance("DSA")
        return keyFactory.generatePrivate(keySpec) as DSAPrivateKey
    }

    /**
     * 解析PEM编码的公钥证书
     *
     * @param filePath 证书文件绝对路径
     * @return DSA公钥对象
     */
    @JvmStatic
    @Throws(IOException::class)
    fun getDSAPublicKeyForPEM(filePath: String): DSAPublicKey? {
        val dsaPublic = FileReader(filePath)
        val publicPem = PEMParser(dsaPublic)
        val pubObject = publicPem.readObject()
        val pemConverter = JcaPEMKeyConverter()
        pemConverter.setProvider(BouncyCastleProvider.PROVIDER_NAME)
        return if (pubObject is SubjectPublicKeyInfo) {
            pemConverter.getPublicKey(pubObject) as DSAPublicKey
        } else {
            null
        }
    }

    /**
     * 解析PEM编码的私钥证书
     *
     * @param filePath 证书文件绝对路径
     * @return DSA私钥对象
     */
    @JvmStatic
    @Throws(IOException::class)
    fun getDSAPrivateKeyForPEM(filePath: String): DSAPrivateKey? {
        val dsaReader = FileReader(filePath)
        val privatePem = PEMParser(dsaReader)
        val obj = privatePem.readObject()
        val pemConverter = JcaPEMKeyConverter()
        pemConverter.setProvider(BouncyCastleProvider.PROVIDER_NAME)
        return if (obj is PEMKeyPair) {
            val keyPair = pemConverter.getKeyPair(obj)
            keyPair.private as DSAPrivateKey
        } else {
            if (obj is KeyPair) obj.private as DSAPrivateKey else null
        }
    }

    /**
     * 解析SSH生成的公钥证书
     *
     * @param keyStr 公钥字符串
     * @return DSA公钥对象
     */
    @JvmStatic
    @Throws(Exception::class)
    @JvmOverloads
    fun getDSAPublicKeyForSSH(keyStr: String, keyEncode: ByteEncodeEnum = ByteEncodeEnum.BASE64): DSAPublicKey {
        val key = decodeToBytes(keyStr, keyEncode)
        val sshDsa = byteArrayOf(
            0,
            0,
            0,
            7,
            's'.code.toByte(),
            's'.code.toByte(),
            'h'.code.toByte(),
            '-'.code.toByte(),
            'd'.code.toByte(),
            's'.code.toByte(),
            's'.code.toByte()
        )
        var startIndex = sshDsa.size/* Decode the p */
        var len = decodeUInt32(key, startIndex)
        startIndex += 4
        val pB = ByteArray(len)
        for (i in 0 until len) {
            pB[i] = key[startIndex++]
        }
        val p = BigInteger(pB)/* Decode the q */
        len = decodeUInt32(key, startIndex)
        startIndex += 4
        val qB = ByteArray(len)
        for (i in 0 until len) {
            qB[i] = key[startIndex++]
        }
        val q = BigInteger(qB)/* Decode the g */
        len = decodeUInt32(key, startIndex)
        startIndex += 4
        val gB = ByteArray(len)
        for (i in 0 until len) {
            gB[i] = key[startIndex++]
        }
        val g = BigInteger(gB)/* Decode the y */
        len = decodeUInt32(key, startIndex)
        startIndex += 4
        val yB = ByteArray(len)
        for (i in 0 until len) {
            yB[i] = key[startIndex++]
        }
        val y = BigInteger(yB)
        val keyFactory = KeyFactory.getInstance("DSA")
        val ks = DSAPublicKeySpec(y, p, q, g)
        return keyFactory.generatePublic(ks) as DSAPublicKey
    }

    /**
     * 生成随机字符串
     *
     * @param flag   类型：RANDOM_STR | RANDOM_CHAR | RANDOM_NUMBER
     * @param length 长度
     * @return 随机字符串
     */
    @JvmStatic
    @JvmOverloads
    fun getRandomString(flag: Int, length: Int, minimumCodePoint: Int = 33, maximumCodePoint: Int = 126): String {
        val builder = RandomStringGenerator.Builder().withinRange(minimumCodePoint, maximumCodePoint)
        when (flag) {
            RANDOM_CHAR -> builder.filteredBy(CharacterPredicates.LETTERS)
            RANDOM_NUMBER -> builder.filteredBy(CharacterPredicates.DIGITS)
            else -> builder.filteredBy(CharacterPredicates.LETTERS, CharacterPredicates.DIGITS)
        }
        return builder.build().generate(length)
    }

    private fun decodeUInt32(key: ByteArray, startIndex: Int): Int {
        val test = key.copyOfRange(startIndex, startIndex + 4)
        return BigInteger(test).toInt()
    }

    /**
     * 读取文件内容
     *
     * @param filePath 文件绝对路径
     * @return 字节数组
     */
    @Throws(IOException::class)
    private fun getFileContent(filePath: String): ByteArray {
        val file = File(filePath)
        if (!file.exists()) {
            throw FileNotFoundException(filePath)
        }
        var inputStream: BufferedInputStream? = null
        var bos: ByteArrayOutputStream? = null
        try {
            bos = ByteArrayOutputStream(file.length().toInt())
            inputStream = BufferedInputStream(FileInputStream(file))
            val bufSize = 1024
            val buffer = ByteArray(bufSize)
            var len: Int = inputStream.read(buffer, 0, bufSize)
            while (-1 != len) {
                bos.write(buffer, 0, len)
                len = inputStream.read(buffer, 0, bufSize)
            }
            return bos.toByteArray()
        } finally {
            inputStream?.close()
            bos?.close()
        }
    }

}